package com.xjj.core.modules.orm.hibernate;

import static org.hibernate.EntityMode.POJO;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Example.PropertySelector;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.xjj.core.modules.commons.utils.MyBeanUtils;
import com.xjj.core.modules.commons.utils.ReflectionUtils;
import com.xjj.core.modules.orm.Condition;
import com.xjj.core.modules.orm.Finder;
import com.xjj.core.modules.orm.Nullable;
import com.xjj.core.modules.orm.OrderBy;
import com.xjj.core.modules.orm.Updater;

/**
 * Hibernate的简单范型基类.
 * 
 * 可以在service类中直接创建使用,也可以继承出DAO子类,在多个Service类中共享DAO操作.
 * 参考Spring2.5自带的Petlinc例子,取消了HibernateTemplate,直接使用Hibernate原生API.
 * 通过Hibernate的sessionFactory.getCurrentSession()获得session.
 * 
 * @param <T> DAO操作的对象类型
 * @param <PK> 主键类型
 * 
 * @author calvin
 */
@SuppressWarnings("unchecked")
public class SimpleHibernateDao<T, PK extends Serializable> {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	protected SessionFactory sessionFactory;

	protected Class<T> entityClass;
	
	/**
	 * 用于扩展的DAO子类使用的构造函数.
	 * 
	 * 通过子类的泛型定义取得对象类型Class.
	 * eg.
	 * public class UserDao extends SimpleHibernateDao<User, Long>
	 */
	public SimpleHibernateDao() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}

	public SimpleHibernateDao(final SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}

	public SimpleHibernateDao(final SessionFactory sessionFactory, final Class<T> entityClass) {
		this.sessionFactory = sessionFactory;
		this.entityClass = entityClass;
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	/**
	 * 增加这个方法才行
	 * 采用@Autowired按类型注入SessionFactory,当有多个SesionFactory的时候Override本函数.
	 */
	@Autowired
	public void setSessionFactory(final SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	/**
	 * 保存新增或修改的对象.
	 */
	public void save(final T entity) {
		Assert.notNull(entity);
		getSession().saveOrUpdate(entity);
		logger.debug("save entity: {}", entity);
	}
	
	public void update(final T entity){
		logger.info("update-----------------------------");
		getSession().update(entity);
	}
	
	//lqz 增加 jeecms 来自：com.ponyjava.common.hibernate3.BaseDaoImpl
	//修改参数:原Object entity
	public Object merge(final T entity) {
		Assert.notNull(entity);
		return getSession().merge(entity);
	}
	
	//增加
	public Class<T> getPersistentClass(){
		return entityClass;
	}
	
	//增加
	public Object persist(final T entity){
		logger.info("Persist for " + getPersistentClass().getSimpleName() + ", using entity = " + entity + ".");
		Assert.notNull(entity);
		getSession().persist(entity);
		logger.info("Persist for " + getPersistentClass().getSimpleName() + ", returning = " + entity + ".");
		return entity;
	}
	

	/**
	 * 删除对象.
	 * 
	 * @param entity 对象必须是session中的对象或含id属性的transient对象.
	 */
	public void delete(final T entity) {
		Assert.notNull(entity);
		logger.info("getSession()-1--- "+getSession());
		logger.info("entity--2-- "+entity);
		getSession().delete(entity);
		logger.debug("delete entity: {}", entity);
	}
	
	public T deleteById(Serializable id) {
		Assert.notNull(id);
		T entity = (T) getSession().load(entityClass, id);
		getSession().delete(entity);
		return entity;
	}

	/**
	 * 按id删除对象.
	 */
	public void delete(final PK id) {
		Assert.notNull(id);
		delete(get(id));
		logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
	}

	/**
	 * 按id获取对象.
	 */
	public T get(final PK id) {
		Assert.notNull(id);
		return (T) getSession().load(entityClass, id);
	}

	/**
	 *	获取全部对象. 
	 */
	public List<T> findAll() {
		return findByCriteria();
	}
	
	//lqz 增加 jeecms 来自：com.ponyjava.common.hibernate3.BaseDaoImpl
	//修改参数:原Serializable id --->final PK id
	//修改的过来的方法一定存在问题，要进行测试和再进行修改
	public T load(final PK id) {
		Assert.notNull(id);
		return load(id, false);
	}
	//lqz 增加 jeecms 来自：com.ponyjava.common.hibernate3.BaseDaoImpl
	//修改参数:原Serializable id --->final PK id
	//   boolean lock ---- >final boolean lock
	//getPersistentClass --->entityClass
	//修改的过来的方法一定存在问题，要进行测试和再进行修改
	
	
	
//	下面两个findAll方法来自jeecms
	//参数修改：原OrderBy... orders--->final OrderBy... orders
	//同时要增加：OrderBy 类
	@SuppressWarnings("unchecked")
	public List<T> findAll(final OrderBy... orders) {
		Criteria crit = createCriteria();
		if (orders != null) {
			for (OrderBy order : orders) {
				crit.addOrder(order.getOrder());
			}
		}
		return crit.list();
	}
	
	/**
	 * 来自jeecms
	 * 修改方法类型：protected----> public
	 * 修改参数: 参数前面全部增加：final
	 * 修改类:getPersistentClass()----->entityClass
	 * 增加Finder 类
	 */
	public Criteria getCritByEg(final T bean, final boolean anyWhere, final Condition[] conds,
			String... exclude) {
		Criteria crit = getSession().createCriteria(entityClass);
		Example example = Example.create(bean);
		example.setPropertySelector(NOT_BLANK);
		if (anyWhere) {
			example.enableLike(MatchMode.ANYWHERE);
			example.ignoreCase();
		}
		for (String p : exclude) {
			example.excludeProperty(p);
		}
		crit.add(example);
		// 处理排序和is null字段
		if (conds != null) {
			for (Condition o : conds) {
				if (o instanceof OrderBy) {
					OrderBy order = (OrderBy) o;
					crit.addOrder(order.getOrder());
				} else if (o instanceof Nullable) {
					Nullable isNull = (Nullable) o;
					if (isNull.isNull()) {
						crit.add(Restrictions.isNull(isNull.getField()));
					} else {
						crit.add(Restrictions.isNotNull(isNull.getField()));
					}
				} else {
					// never
				}
			}
		}
		// 处理many to one查询
		ClassMetadata cm = getCmd(bean.getClass());
		String[] fieldNames = cm.getPropertyNames();
		for (String field : fieldNames) {
			Object o = cm.getPropertyValue(bean, field, POJO);
			if (o == null) {
				continue;
			}
			ClassMetadata subCm = getCmd(o.getClass());
			if (subCm == null) {
				continue;
			}
			Serializable id = subCm.getIdentifier(o, POJO);
			if (id != null) {
				Serializable idName = subCm.getIdentifierPropertyName();
				crit.add(Restrictions.eq(field + "." + idName, id));
			} else {
				crit.createCriteria(field).add(Example.create(o));
			}
		}
		return crit;
	}
	
	/**
	 * 来自jeecms
	 * 修改方法类型：protected----> public
	 * 修改参数: 参数前面全部增加：final
	 */
	public void refresh(Object entity) {
		getSession().refresh(entity);
	}
	
	@SuppressWarnings("unchecked")
	public T load(final PK id, final boolean lock) {
		Assert.notNull(id);
		T entity = null;
		if (lock) {
			entity = (T) getSession().load(entityClass, id,
					LockMode.UPGRADE);
		} else {
			entity = (T) getSession().load(entityClass, id);
		}
		return entity;
	}

	/**
	 * 按属性查找对象列表,匹配方式为相等.
	 */
	public List<T> findByProperty(final String propertyName, final Object value) {
		Assert.hasText(propertyName);
		Criterion criterion = Restrictions.eq(propertyName, value);
		return findByCriteria(criterion);
	}

	/**
	 * 按属性查找唯一对象,匹配方式为相等.
	 */
	public T findUniqueByProperty(final String propertyName, final Object value) {
		Assert.hasText(propertyName);
		Criterion criterion = Restrictions.eq(propertyName, value);
		return (T) createCriteria(criterion).uniqueResult();
	}

	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param hql hql语句
	 * @param values 数量可变的参数
	 */
	public List find(final String hql, final Object... values) {
		return createQuery(hql, values).list();
	}

	/**
	 * 按HQL查询唯一对象.
	 */
	public Object findUnique(final String hql, final Object... values) {
		return createQuery(hql, values).uniqueResult();
	}

	/**
	 * 按HQL查询Integer类型结果. 
	 */
	public Integer findInt(final String hql, final Object... values) {
		return (Integer) findUnique(hql, values);
	}

	/**
	 * 按HQL查询Long类型结果. 
	 */
	public Long findLong(final String hql, final Object... values) {
		return (Long) findUnique(hql, values);
	}

	/**
	 * 按Criteria查询对象列表.
	 * 
	 * @param criterions 数量可变的Criterion.
	 */
	public List<T> findByCriteria(final Criterion... criterions) {
		return createCriteria(criterions).list();
	}

	/**
	 * 判断对象的属性值在数据库内是否唯一.
	 * 
	 * 适用于ajax场景，在修改对象的情景下,如果属性新修改的值(value)等于属性原来的值(orgValue)则不作比较.
	 */
	public boolean isPropertyUnique(final String propertyName, final Object newValue, final Object orgValue) {
		if (newValue == null || newValue.equals(orgValue))
			return true;
		Object object = findUniqueByProperty(propertyName, newValue);
		return (object == null);
	}

	/**
	 * 根据查询HQL与参数列表创建Query对象,辅助函数.
	 */
	public Query createQuery(final String queryString, final Object... values) {
		Assert.hasText(queryString);
		Query queryObject = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		return queryObject;
	}

	/**
	 * 根据Criterion条件创建Criteria,辅助函数.
	 */
	public Criteria createCriteria(final Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 取得对象的主键名,辅助函数.
	 */
	public String getIdName() {
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		Assert.notNull(meta, "Class " + entityClass.getSimpleName() + " not define in hibernate session factory.");
		return meta.getIdentifierPropertyName();
	}
	
	/**
	 * 来自：jeecms
	 * 
	 * @param updater
	 * @param po
	 */
	public Object updateByUpdater(Updater updater) {
		ClassMetadata cm = getCmd(updater.getBean().getClass());
		if (cm == null) {
			throw new RuntimeException("所更新的对象没有映射或不是实体对象");
		}
		Object bean = updater.getBean();
		Object po = getSession().load(bean.getClass(),
				cm.getIdentifier(bean, POJO));
		updaterCopyToPersistentObject(updater, po);
		return po;
	}

	/**
	 * 来自：jeecms
	 * 将更新对象拷贝至实体对象，并处理many-to-one的更新。
	 * 
	 * @param updater
	 * @param po
	 */
	@SuppressWarnings("unchecked")
	private void updaterCopyToPersistentObject(Updater updater, Object po) {
		Map map = MyBeanUtils.describe(updater.getBean());
		Set<Map.Entry<String, Object>> set = map.entrySet();
		for (Map.Entry<String, Object> entry : set) {
			String name = entry.getKey();
			Object value = entry.getValue();
			if (!updater.isUpdate(name, value)) {
				continue;
			}
			if (value != null) {
				Class valueClass = value.getClass();
				ClassMetadata cm = getCmd(valueClass);
				if (cm != null) {
					Serializable vid = cm.getIdentifier(value, POJO);
					// 如果更新的many to one的对象的id为空，则将many to one设置为null。
					if (vid != null) {
						value = getSession().load(valueClass, vid);
					} else {
						value = null;
					}
				}
			}
			try {
				PropertyUtils.setProperty(po, name, value);
			} catch (Exception e) {
				// never
				logger.warn("更新对象时，拷贝属性异常", e);
			}
		}
	}
	
	/**
	 * 增加来自jeecms
	 * 通过count查询获得本次查询所能获得的对象总数.
	 * 要增加Finder 这个类
	 * 修改参数为：Finder finder ---- final Finder finder
	 * @param finder
	 * @return
	 */
	protected int countQueryResult(final Finder finder) {
		//logger.info("finder.getRowCountHql()---- "+finder.getRowCountHql());
		Query query = getSession().createQuery(finder.getRowCountHql());
		finder.setParamsToQuery(query);
		return ((Number) query.iterate().next()).intValue();
	}
	/**
	 * 增加来自jeecms
	 * 通过count查询获得本次查询所能获得的对象总数.
	 * 要增加Finder 这个类
	 * 修改参数为：
	 * @param getPersistentClass()---- > entityClass
	 * @return
	 */
	public T createNewEntiey() {
		try {
			return entityClass.newInstance();
		} catch (Exception e) {
			throw new RuntimeException("不能创建实体对象："
					+ entityClass.getName());
		}
	}
	
	/**
	 * 增加来自jeecms
	 * 通过count查询获得本次查询所能获得的对象总数.
	 * 要增加Finder 这个类
	 * 修改参数为：
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private ClassMetadata getCmd(Class clazz) {
		return (ClassMetadata) sessionFactory.getClassMetadata(clazz);
	}
	
	//增加来自jeecms
	public static final NotBlankPropertySelector NOT_BLANK = new NotBlankPropertySelector();

	/**
	 * 增加来自jeecms
	 * 不为空的EXAMPLE属性选择方式
	 * 
	 * @author liufang
	 * 
	 */
	static final class NotBlankPropertySelector implements PropertySelector {
		private static final long serialVersionUID = 1L;

		public boolean include(Object object, String propertyName, Type type) {
			return object != null
					&& !(object instanceof String && StringUtils
							.isBlank((String) object));
		}
	}
	
	//-----springside3新方法--
	
	/**
	 * 初始化对象.
	 * 使用load()方法得到的仅是对象Proxy后, 在传到View层前需要进行初始化.
	 * initObject(user) ,初始化User的直接属性，但不会初始化延迟加载的关联集合和属性.
	 * initObject(user.getRoles())，初始化User的直接属性和关联集合.
	 * initObject(user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
	 */
	public void initObject(Object object) {
		Hibernate.initialize(object);
	}

	/**
	 * 批量初始化对象.
	 * @see #initObject(Object)
	 */
	public void initObjects(List list) {
		for (Object object : list) {
			Hibernate.initialize(object);
		}
	}

	/**
	 * 为Query添加distinct transformer.
	 */
	public Query distinct(Query query) {
		query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return query;
	}

	/**
	 * 为Criteria添加distinct transformer.
	 */
	public Criteria distinct(Criteria criteria) {
		criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return criteria;
	}

	/**
	 * 通过Set将不唯一的对象列表唯一化.
	 * 主要用于HQL/Criteria预加载关联集合形成重复记录,又不方便使用distinct查询语句时.
	 */
	public <X> List<X> distinct(List list) {
		Set<X> set = new LinkedHashSet<X>(list);
		return new ArrayList<X>(set);
	}

}
